package com.example.esplugins.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.esplugins.exception.EsPluginsException;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @Classname EsUtils
 * @Description ES工具类
 * @Date 2022/4/27 10:21
 * @Created by Qinger
 */
@Component
@Slf4j
public class EsUtils {
    @Resource
    /*配置信息参考src/main/java/com/example/esplugins/config/ElasticSearchConfig.java*/
    private RestHighLevelClient restHighLevelClient;
    @Value("${es.numberOfShards:5}")
    private Integer numberOfShards;
    @Value("${es.numberOfReplicas:1}")
    private Integer numberOfReplicas;
    @Value("${es.timeOut:1}")
    private Integer timeOut;

    /*判断索引是否存在 */
    private Boolean ifIndexExist(String index) throws IOException {
        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
        return restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
    }

    /*创建索引*/
    public Boolean createIndex(String index) throws IOException {
        // 首先判断索引是否已经存在
        if (ifIndexExist(index)) {
            throw new EsPluginsException("目标索引: " + index + " 已存在", "index_is_exist");
        }
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
        createIndexRequest.settings(Settings.builder().put("index.number_of_replicas", numberOfReplicas).put("index.number_of_shards", numberOfShards));
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        return createIndexResponse.isAcknowledged();
    }

    /*删除索引*/
    public Boolean deleteIndex(String index) throws IOException {
        // 判断索引是否存在
        if (!ifIndexExist(index)) {
            throw new EsPluginsException("目标索引: " + index + "不存在", "index_not_exist");
        }
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
        AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        return delete.isAcknowledged();
    }

    /*保存文档*/
    public Boolean save(Object obj, String index, String id) throws IOException {
        try {
            createIndex(index);
        } catch (EsPluginsException esPluginsException) {
            Assert.isTrue(esPluginsException.getErrCode().equals("index_is_exist"), esPluginsException.getMessage());
        }
        IndexRequest indexRequest = new IndexRequest(index);
        indexRequest.id(id);
        indexRequest.timeout(TimeValue.timeValueSeconds(timeOut));
        // 将对象转为json
        String document = JSON.toJSONString(obj);
        indexRequest.source(document, XContentType.JSON);
        // 发送请求
        IndexResponse result = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        return result.getResult() == DocWriteResponse.Result.CREATED || result.getResult() == DocWriteResponse.Result.UPDATED;
    }

    /*保存文档,自动生成ID*/
    public Boolean save(Object obj, String index) throws IOException {
        try {
            createIndex(index);
        } catch (EsPluginsException esPluginsException) {
            Assert.isTrue(esPluginsException.getErrCode().equals("index_is_exist"), esPluginsException.getMessage());
        }
        IndexRequest indexRequest = new IndexRequest(index);
        indexRequest.id(UUID.randomUUID().toString());
        indexRequest.timeout(TimeValue.timeValueSeconds(timeOut));
        // 将对象转为json
        String document = JSON.toJSONString(obj);
        indexRequest.source(document, XContentType.JSON);
        // 发送请求
        IndexResponse result = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        return result.getResult() == DocWriteResponse.Result.CREATED || result.getResult() == DocWriteResponse.Result.UPDATED;
    }

    /*删除数据*/
    public Boolean deleteDoc(String index, String id) throws IOException {
        DeleteRequest deleteIndexRequest = new DeleteRequest(index, id);
        DeleteResponse result = restHighLevelClient.delete(deleteIndexRequest, RequestOptions.DEFAULT);
        return result.getResult() == DocWriteResponse.Result.DELETED || result.getResult() == DocWriteResponse.Result.NOT_FOUND;
    }

    /*更新文档数据*/
    public Boolean updateDoc(Object obj, String index, String id) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest(index, id);
        String jsonDoc = JSONObject.toJSONString(obj);
        updateRequest.doc(jsonDoc, XContentType.JSON);
        updateRequest.timeout(TimeValue.timeValueSeconds(timeOut));
        UpdateResponse update = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        return update.getResult() == DocWriteResponse.Result.UPDATED || update.getResult() == DocWriteResponse.Result.NOOP;
    }

    /*根据id获取数据(全部字段)*/
    public Map<String, Object> getById(String index, String id) throws IOException {
        GetRequest getIndexRequest = new GetRequest(index, id);
        GetResponse documentFields = restHighLevelClient.get(getIndexRequest, RequestOptions.DEFAULT);
        Map<String, Object> source = documentFields.getSource();
        return source;
    }

    /*根据id获取数据(全部字段)*/
    public <T> T getById(String index, String id, Class<? extends T> clazz) throws IOException {
        GetRequest getIndexRequest = new GetRequest(index, id);
        GetResponse documentFields = restHighLevelClient.get(getIndexRequest, RequestOptions.DEFAULT);
        return JSONObject.parseObject(JSONObject.toJSONString(documentFields.getSource()), clazz);
    }

    /* 查询文档是否存在*/
    public Boolean ifDocExists(String index, String id) throws IOException {
        GetRequest getRequest = new GetRequest(index, id);
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        return restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
    }


    /*批量插入文档 随机ID*/
    public Boolean batchSaveDoc(String index, List<? extends Object> list) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (Object docObject : list) {
            IndexRequest indexRequest = new IndexRequest(index);
            indexRequest.source(JSONObject.toJSONString(docObject), XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !bulk.hasFailures();
    }

    /*条件查询 支持分页*/
    public SearchHits search(String index, SearchSourceBuilder searchSourceBuilder) throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.source(searchSourceBuilder);
        searchRequest.indices(index);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return search.getHits();
    }
}
